// [[ OnflowGenerator File UUID: 1bc7004d10284bab877dc517222f5867  ]]
package com.itdct.onflow.system.menu.service

import cn.hutool.core.lang.UUID
import com.baomidou.mybatisplus.extension.kotlin.KtQueryWrapper
import com.itdct.onflow.core.constant.CommonString
import com.itdct.onflow.core.constant.ReturnCode
import com.itdct.onflow.core.extend.*
import com.itdct.onflow.core.vo.KeyLabelValueChildrenVo
import com.itdct.onflow.core.vo.KeyLabelValueVo
import com.itdct.onflow.core.vo.RespPageVo
import com.itdct.onflow.core.vo.RespVo
import com.itdct.onflow.system.common.base.BaseSysService
import com.itdct.onflow.system.common.context.Context
import com.itdct.onflow.system.common.extend.dict
import com.itdct.onflow.system.component.entity.SysComponentEntity
import com.itdct.onflow.system.component.mapper.SysComponentMapper
import com.itdct.onflow.system.menu.entity.SysMenuEntity
import com.itdct.onflow.system.menu.mapper.SysMenuMapper
import com.itdct.onflow.system.menu.qo.SysMenuEditQo
import com.itdct.onflow.system.menu.qo.SysMenuSearchQo
import com.itdct.onflow.system.menu.vo.MenuRoleMetaVo
import com.itdct.onflow.system.menu.vo.MenuRoleVo
import com.itdct.onflow.system.menu.vo.SysMenuDetailVo
import com.itdct.onflow.system.menu.vo.SysMenuListVo
import com.itdct.onflow.system.role.entity.SysRoleComponentEntity
import com.itdct.onflow.system.role.mapper.SysRoleComponentMapper
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional

/**
 * @author DCTANT
 * @date 2025-09-01 14:35:02
 * @version 1.0
 * @description 系统菜单服务
 */
@Service
class SysMenuService(
    val sysMenuMapper: SysMenuMapper,
    val sysRoleComponentMapper: SysRoleComponentMapper,
    val sysComponentMapper: SysComponentMapper
) : BaseSysService() {

    @Transactional(rollbackFor = [Exception::class])
    fun add(qo: SysMenuEditQo, context: Context): RespVo<Long> {
        val parentId = qo.parentId ?: 0L
        qo.parentId = parentId

        val addEntity = qo.toEntity(SysMenuEntity::class.java)
        setIdPath(addEntity)
        val id = sysMenuMapper.insertEx(addEntity)
        return success(id)
    }

    private fun setIdPath(sysMenuEntity: SysMenuEntity) {
        val pathIds = ArrayList<Long>()
        var parentId = sysMenuEntity.parentId
        while (parentId != 0L) {
            pathIds.add(parentId)
            var parentEntity = sysMenuMapper.selectByIdEx(parentId)
            parentId = parentEntity.parentId
        }
        pathIds.reverse()
        if (pathIds.isEmpty()) {
            sysMenuEntity.idPath = ""
        } else {
            sysMenuEntity.idPath = pathIds.joinToString("/")
        }
    }

    @Transactional(rollbackFor = [Exception::class])
    fun update(qo: SysMenuEditQo, context: Context): RespVo<Unit> {
        val parentId = qo.parentId ?: 0L
        qo.parentId = parentId

        val updateEntity = sysMenuMapper.selectByIdEx(qo.id)
        qo.injectValue(updateEntity)
        setIdPath(updateEntity)
        sysMenuMapper.updateEx(updateEntity)

        refreshAllIdPath()
        return success()
    }

    private fun refreshAllIdPath() {
        // INFO: Zhouwx: 2025/9/5 如果修改菜单层级，则需要修改所有菜单的id path
        sysMenuMapper.selectAll().forEach {
            setIdPath(it)
            sysMenuMapper.updateEx(it)
        }
    }

    fun delete(qo: SysMenuEditQo, context: Context): RespVo<Unit> {
        val ids = qo.ids ?: fail(ReturnCode.LACK_PARAM, CommonString.REQUIRE_DELETE_ID_LIST)
        val deleteEntities = sysMenuMapper.selectByIdsEx(ids)
        sysMenuMapper.deleteBatchEx(deleteEntities)
        return success()
    }

    fun detail(qo: SysMenuSearchQo, context: Context): RespVo<SysMenuDetailVo> {
        val id = qo.id ?: fail(ReturnCode.LACK_PARAM, CommonString.REQUIRE_ID)
        val vo = sysMenuMapper.findById(id) ?: fail(CommonString.ENTITY_NOT_FOUND_USAGE("系统菜单", id))
        return success(vo.dict())
    }

    fun page(qo: SysMenuSearchQo, context: Context): RespPageVo<SysMenuListVo> {
        startPage(context)
        val pageData = sysMenuMapper.findByList(qo).dict()
        val listVoMap = pageData.associateBy { it.id }
        clearPage(context)
        val sysMenuSearchQo = SysMenuSearchQo()
        sysMenuSearchQo.exceptRoot = true
        val findByList = sysMenuMapper.findByList(sysMenuSearchQo).dict()
        val notRootListVoMap = findByList.associateBy { it.id }

        val totalMap = HashMap<Long, SysMenuListVo>()
        totalMap.putAll(listVoMap)
        totalMap.putAll(notRootListVoMap)

        for (sysMenuListVo in findByList) {
            val parentId = sysMenuListVo.parentId
            totalMap[parentId]?.also {
                it.children.add(sysMenuListVo)
                it.children.sortBy { it.orderNum }
            }
        }

        return successPage(pageData.dict())
    }

    @Transactional(rollbackFor = [Exception::class])
    fun clone(qo: SysMenuEditQo, context: Context): RespVo<Unit> {
        val id = qo.id ?: fail(ReturnCode.LACK_PARAM, CommonString.REQUIRE_ID)
        val findEntity = sysMenuMapper.selectByIdEx(id)
        findEntity.id = null
        val uuid = UUID.fastUUID().toString(true).substring(0, 4)
        findEntity.name = findEntity.name + "_" + uuid
        findEntity.title = findEntity.title + "_" + uuid

        sysMenuMapper.insertEx(findEntity)
        return success()
    }

    fun chooseTitleCascade(qo: SysMenuSearchQo, context: Context): RespVo<List<KeyLabelValueChildrenVo>> {
        val queryWrapper = KtQueryWrapper(SysMenuEntity::class.java)
        queryWrapper.ne(qo.excludeId != null, SysMenuEntity::id, qo.excludeId)
            .orderByAsc(SysMenuEntity::parentId)
            .orderByAsc(SysMenuEntity::orderNum)
            .orderByDesc(SysMenuEntity::createTime)
        val menuEntities = sysMenuMapper.selectList(queryWrapper)
        val groupBy = menuEntities.groupBy { it.id }
        val list = ArrayList<KeyLabelValueChildrenVo>()
        val voMap = HashMap<Long, KeyLabelValueChildrenVo>()
        for (menuEntity in menuEntities) {
            val parentId = menuEntity.parentId
            if (parentId == 0L) {
                val vo = KeyLabelValueChildrenVo()
                vo.key = menuEntity.id!!
                vo.label = menuEntity.title + "(${menuEntity.name})"
                vo.value = menuEntity.id
                list.add(vo)
                voMap[menuEntity.id!!] = vo
            } else {
                val parentMenu = groupBy[parentId]?.firstOrNull()
                if (parentMenu != null) {
                    val vo = KeyLabelValueChildrenVo()
                    vo.key = menuEntity.id!!
                    vo.label = menuEntity.title + "(${menuEntity.name})"
                    vo.value = menuEntity.id
                    val parentVo = voMap[menuEntity.parentId]
                    voMap[menuEntity.id!!] = vo
                    parentVo?.let {
                        parentVo.children.add(vo)
                    }
                }
            }
        }
        return success(list)
    }

    fun roleMenuList(qo: SysMenuSearchQo, context: Context): RespVo<List<MenuRoleVo>> {
        val platformType = qo.platformType ?: fail(ReturnCode.LACK_PARAM, "请输入平台类型")
        val accountId = context.accountId ?: fail(ReturnCode.UNAUTHORIZED, CommonString.NEED_LOGIN)
        val loginDTO = context.loginDTO ?: fail(ReturnCode.UNAUTHORIZED, CommonString.NEED_LOGIN)
        val roleId = loginDTO.roleId
        var menuList: List<SysMenuEntity>
        if (loginDTO.superAdmin) {
            // INFO: Zhouwx: 2025/8/25 超级管理员，直接获取所有菜单
            menuList = sysMenuMapper.getAllMenu(platformType)
        } else {
            // INFO: Zhouwx: 2025/8/25 非超管，需要获取对应角色的菜单
            menuList = sysMenuMapper.getRoleMenu(roleId, platformType)
        }

        var wholeMenuList = ArrayList<SysMenuEntity>()
        wholeMenuList.addAll(menuList)

        // INFO: Zhouwx: 2025/9/15 必须关联到根菜单，否则整个页面都是空的
        val allMenuList = sysMenuMapper.selectAll()
        val allMenuMap = allMenuList.associateBy { it.id }
        for (sysMenuEntity in menuList) {
            var parentId = sysMenuEntity.parentId
            while (true) {
                if (parentId == 0L) {
                    break
                }
                val parentMenu = allMenuMap[parentId]
                if (parentMenu != null) {
                    parentId = parentMenu.parentId
                    val id = parentMenu.id!!
                    if (wholeMenuList.findLast { it.id == id } == null) {
                        wholeMenuList.add(parentMenu)
                    }
                } else {
                    break
                }
            }
        }

        menuList = wholeMenuList.sortedWith(
            compareBy(
                { if (it.idPath.isBlank()) -1 else it.idPath.split("/").size },
                { it.orderNum }
            )
        )

        val roleMenuVos = ArrayList<MenuRoleVo>()
        val roleMenuVoMap = HashMap<Long, MenuRoleVo>()
        val menuMap = menuList.associateBy { it.id }
        val parentIdGroup = menuList.groupBy { it.parentId }

        val menuIdComponentMap = HashMap<Long, List<SysRoleComponentEntity>>()
        val sysComponentMap = HashMap<Long, SysComponentEntity>()
        if (!loginDTO.superAdmin) {
            // INFO: Zhouwx: 2025/9/18 非超级管理员需要获取组件权限
            val roleComponentEntities = sysRoleComponentMapper.findByRoleId(roleId)
            val menuIdGroupBy = roleComponentEntities.groupBy { it.menuId }
            menuIdComponentMap.putAll(menuIdGroupBy)
            val tempSysComponentMap = sysComponentMapper.selectAll().associateBy { it.id!! }
            sysComponentMap.putAll(tempSysComponentMap)
        }

        for (menu in menuList) {
            val parentId = menu.parentId
            val menuRoleVo = MenuRoleVo()
            val name = menu.name
            menuRoleVo.name = name
            if (parentId == 0L) {
                menuRoleVo.path = "/" + name
            } else {
                menuRoleVo.path = name
            }

            var component = menu.component
            if (component.isBlank()) {
                if (parentId == 0L) {
                    component = "#"
                } else {
                    component = "##"
                }
            } else {
                if (component.startsWith("/")) {
                    component = component.substring(1)
                }
                if (!component.startsWith("views")) {
                    component = "views/$component"
                }

                if (component.endsWith(".vue")) {
                    component = component.substring(0, component.length - 4)
                }
            }
            menuRoleVo.component = component
            val menuRoleMetaVo = MenuRoleMetaVo()
            menuRoleMetaVo.title = menu.title
            menuRoleMetaVo.icon = menu.icon
            menuRoleMetaVo.hidden = menu.hiddenMenu
            menuRoleMetaVo.menuId = menu.id
            menuRoleMetaVo.menuCode = menu.name
            menuRoleMetaVo.noTagsView = menu.hiddenTag
            if (!loginDTO.superAdmin) {
                // INFO: Zhouwx: 2025/9/18 非超级管理员分配其按钮组件权限
                menuIdComponentMap[menu.id!!]?.also {
                    val codeList = it.map {
                        val componentId = it.componentId
                        val sysComponentEntity = sysComponentMap[componentId]
                        sysComponentEntity
                    }.filterNotNull()
                        .map { it.code }
                    menuRoleMetaVo.components = codeList
                }
            }

            menuRoleVo.meta = menuRoleMetaVo
            if (parentId == 0L) {
                roleMenuVos.add(menuRoleVo)
                roleMenuVoMap[menu.id!!] = menuRoleVo
            } else {
                val parentMenu = roleMenuVoMap[parentId]
                parentMenu?.let { parent ->
                    parent.children?.also {
                        it.add(menuRoleVo)
                    } ?: also {
                        parent.children = ArrayList()
                        parent.children?.add(menuRoleVo)

                    }
                }
                roleMenuVoMap[menu.id!!] = menuRoleVo
            }

            if (menuRoleMetaVo.hidden == true) {
                // INFO: Zhouwx: 2025/4/2 如果是隐藏的，则需要赋值activeMenu
                val siblingMenuEntities = parentIdGroup[parentId]
                val haveComponentSiblings = siblingMenuEntities?.filter {
                    it.component.isNotBlank() && !it.hiddenMenu
                }
                if (haveComponentSiblings == null || haveComponentSiblings.isEmpty()) {
                    // INFO: Zhouwx: 2025/4/2 没有同级的有值，那就找上一级
                    var parentVo: MenuRoleVo? = null
                    val activeMenuStrings = ArrayList<String>()
                    var tempMenu = menu
                    while (true) {
                        if (tempMenu == null) {
                            break
                        }
                        val parentId = tempMenu.parentId
                        if (parentId == 0L) {
                            break
                        } else {
                            parentVo = roleMenuVoMap[parentId]
                            menuMap[parentId]?.also {
                                tempMenu = it
                            }
                            parentVo?.let {
                                activeMenuStrings.add(0, it.path)
                            }

                        }
                    }
                    menuRoleMetaVo.activeMenu = activeMenuStrings.joinToString("/")
                } else {
                    // INFO: Zhouwx: 2025/4/2 同一级有，说明就是同一级的要被激活
                    val suffix = name.toUnderlineCase().substring(0, name.toUnderlineCase().lastIndexOf("_"))
                    val needActiveEntity = haveComponentSiblings.filter {
                        it.name.toUnderlineCase().substring(0, it.name.toUnderlineCase().lastIndexOf("_")) == suffix
                    }.firstOrNull()
                    if (needActiveEntity != null) {
                        val activeMenuStrings = ArrayList<String>()
                        val findMenuRoleVo = roleMenuVoMap[needActiveEntity.id!!]
                        if (findMenuRoleVo != null) {
                            activeMenuStrings.add(findMenuRoleVo.path)
                        }
                        var tempMenu = needActiveEntity
                        var parentVo: MenuRoleVo? = null
                        while (true) {
                            if (tempMenu == null) {
                                break
                            }
                            val parentId = tempMenu!!.parentId
                            if (parentId == 0L) {
                                break
                            } else {
                                parentVo = roleMenuVoMap[parentId]
                                tempMenu = menuMap[parentId]
                                parentVo?.let {
                                    activeMenuStrings.add(0, it.path)
                                }
                            }
                        }
                        menuRoleMetaVo.activeMenu = activeMenuStrings.joinToString("/")
                    }
                }
            }
        }
        return success(roleMenuVos)
    }

// [[ OnflowGenerator Range (- selectFunctions -) Start ]]

    fun selectTitleList(qo: SysMenuSearchQo, context: Context): RespVo<List<KeyLabelValueVo>> {
        val vos = sysMenuMapper.selectTitleList(qo)
        return success(vos)
    }

    fun selectNameList(qo: SysMenuSearchQo, context: Context): RespVo<List<KeyLabelValueVo>> {
        val vos = sysMenuMapper.selectNameList(qo)
        return success(vos)
    }
// [[ OnflowGenerator Range (- selectFunctions -) End ]]
}